package com.gxs.ea.examples;

import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;








import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

public class ParseGxsEventBuildTree implements Comparator<Element>
{
	
	private Map<String, Document> mapOfDocumentsByCorrelationId = new HashMap<String, Document>();
	

	/**
	 * Usage:
	 *  
	 * Extract all events (<gxsevents>)
	 * Create a new xml file, with root node of <events>, and put all <gxsevents> as children of that root node
	 * Name this input file appropriately so as to identify the scenario
	 * 
	 * Then specify the input file name below
	 * 
	 * Run the program
	 * 
	 * The proram will produce the output file in the same location as the input file, by adding a -out suffix to the input file name. 
	 * 
	 * Rinse
	 * 
	 * Repeat
	 */

	// The translation scenario was executed on June 20th 2014 with a correlation id of 47ac5e0f-16a9-453b-b626-4ffb29cbc457
	// It has not been purged out of systems yet and can still be seen in TGMS/orchestration/ActiveDocs. 
	// In Orchestration UI, this can be accessed at the following URL: 
	// http://q1s1dui1.qa.gxsonline.net:8080/orchWeb/jsf/process.xhtml?itinInstanceId=ddf2fead-2fc9-45a0-a5ee-f3b054425c64&init=true
	private static String tgxxTranslationScenarioInputFile = "tgxx-translation-scenario/allEvents-in.xml";
	
	//
	// this is a pass through scenario which was executed in March and is purged out of systems now, so it can't be viewed live in TGMS/Biz/AD/Orch UIs. 
	// (Unless we run a new test)
	private static String tgxxPassThroughScenarioInputFile = "tgxx-passthrough-scenario/tgxx-scenario1.xml";
	

	//
	// specify the scneario input file to parse and process
	private static String inputFileName = tgxxTranslationScenarioInputFile;
	
	private ProcessTree tree = new ProcessTree();
	
	public static void main(String [] args)
	{
		try
		{
			 (new ParseGxsEventBuildTree()).parseFileAndReportStats(inputFileName);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	private String  processASingleEvent(Element singleEvent)
	{
		Element header = getFirstChildByNameRegardlessOfNameSpace("gxsevent", singleEvent, "header");
		
		if(header == null)
		{
			System.out.println("\t\theader was found to be null for this event: \n\n" + singleEvent.getText());
			System.exit(1);
		}
		
		String correlationId = getFirstChildByNameRegardlessOfNameSpace("header", header, "correlationid").getText();
		System.out.println("\t\tCorrelation id is: " + correlationId);
		Element correlation = new Element("correlationId");
		correlation.setAttribute("value", correlationId);

		
		Document newDoc = null;
		
		if(mapOfDocumentsByCorrelationId.get(correlationId) == null)
		{
			newDoc = new Document();
			mapOfDocumentsByCorrelationId.put(correlationId, newDoc);
			newDoc.addContent(correlation);
		}
		
		Element identifiers = getFirstChildByNameRegardlessOfNameSpace("header", header, "processidentifiers");
			
		Element pid = getFirstChildByNameRegardlessOfNameSpace("identifiers", identifiers, "identifier");
		Element ppid = getFirstChildByNameRegardlessOfNameSpace("identifiers", identifiers, "parentidentifier");
		
		if(pid == null)
		{
			System.err.println("Skipping current event because PID is not present");
			return correlationId;
		}
		System.out.println("\t\tPID and PPID are: " + pid.getText() + "  ---  " + ppid.getText());
			
		singleEvent.detach();
		
		tree.addProcessEvent(pid.getText(),  ppid.getText(), singleEvent);

		System.out.println("----------------------------------------------------------------\n");
		return correlationId;
	}

	private  void parseFileAndReportStats(String fileName) throws Exception
	{
		SAXBuilder builder = new SAXBuilder();
		
		Document doc = (Document) builder.build(this.getClass().getClassLoader().getResourceAsStream(fileName));
//		Document doc = (Document) builder.build(fileName);
		
		
		Element rootNode = doc.getRootElement();
		
		System.out.println(rootNode + " is the root element");
		
		if(! rootNode.getName().equals("events"))
		{
			System.out.println("Root node was expected to be <events> -- please fix and retry. ");
			System.exit(1);
		}
		
		List<Element> allEvents = getListOfChildrenMatchingNameRegardlessOfNameSpace(rootNode, "gxsevent");
		
		System.out.println("Found: " + allEvents.size() + " events in the given file");
		
		System.out.println("----------------------------------------------------------------\n");
		 
		int counter = 0;
		for(Element event : allEvents)
		{
			System.out.println("Processing Event Number: " + (counter+1));
			processASingleEvent(event);
			counter++;
		}
		
		System.out.println("Total Events Processed: " + counter);
		System.out.println("total number of correlationIds found: " + mapOfDocumentsByCorrelationId.keySet().size());
		
		
		if(mapOfDocumentsByCorrelationId.keySet().size() > 1)
		{
			System.out.println("ERROR: More than 1 correlation ids found. cannot proceed...");
			System.exit(1);
		}
		
		
		for( String correlationId : mapOfDocumentsByCorrelationId.keySet() )
		{
			Document newDoc = mapOfDocumentsByCorrelationId.get(correlationId);
			XMLOutputter xmlOutput = new XMLOutputter();
		
			//
			// find parent: 
			System.out.println("The root process id is: " + tree.validateTreeHasSingleParent());
			ProcessTreeNode topOfTree = tree.buildTree();

			//
			Element correlationElement = newDoc.getRootElement();
			//
			// Tree has been built - now create the XML
			addProcessContentToXMLNode(correlationElement, topOfTree);
			
			
			// display nice nice
			xmlOutput.setFormat(Format.getPrettyFormat());

			xmlOutput.output(newDoc, new FileWriter( "src/main/resources/" + fileName + "-out.xml"));
			System.out.println("File Saved!");
		}
		
	}
	
	private void addProcessContentToXMLNode(Element head,
			ProcessTreeNode currentNode) 
	{
		//
		// First add the the currentNode itself as a process entry
		Element process = new Element("process");
		process.setAttribute("processId", currentNode.getProcessId());
		process.setAttribute("parentProcessId", currentNode.getParentProcessId() == null ? "null" : currentNode.getParentProcessId());
		int numberOfEvent = currentNode.getEvents() == null ? 0 : currentNode.getEvents().size();
		process.setAttribute("numberOfEvents", String.valueOf(numberOfEvent));
		
		Element events = new Element("events");

		Collections.sort(currentNode.getEvents(), this);

		
		for(Element currentEvent : currentNode.getEvents() )
		{
			events.addContent(currentEvent);
			Element eventHeader = getFirstChildByNameRegardlessOfNameSpace("gxsevent", currentEvent, "header");
			Element eventName = getFirstChildByNameRegardlessOfNameSpace("header", eventHeader, "eventname");
			Element identifiers = getFirstChildByNameRegardlessOfNameSpace("header", eventHeader,"processidentifiers");
			Element sequenceId = getFirstChildByNameRegardlessOfNameSpace("identifiers", identifiers, "sequenceid");
			Element eventId = getFirstChildByNameRegardlessOfNameSpace("header", eventHeader, "eventid");
			currentEvent.setAttribute("eventName", eventName.getText());
			currentEvent.setAttribute("sequenceId", sequenceId.getText());
			currentEvent.setAttribute("eventId", eventId.getText());
			
		}

		if(numberOfEvent > 0)
			process.addContent(events);

		
		head.addContent(process);

		for(ProcessTreeNode childNode : currentNode.getChildProcesses() )
		{
			addProcessContentToXMLNode(process, childNode);
		}
	
	}

	private List<Element> getListOfChildrenMatchingNameRegardlessOfNameSpace(	Element givenNode, String childName) 
	{
		List<Element> list = new ArrayList<Element>();
		List<Element> allChildren = givenNode.getChildren();
		
		for(Element e : allChildren )
		{
			if(e.getName().equals(childName))
			{
				list.add(e);
			}
		}
		
		return list;
		
	}

	private  Element getFirstChildByNameRegardlessOfNameSpace(String nameOfParent, Element givenNode, String name)
	{
		try
		{
			List<Element> children = givenNode.getChildren();

			if(children != null)
			{
				for(Element e : children)
				{
					if(e.getName().equals(name))
					{
						return e;
					}
				}
			}
		
			return null;
		}
		catch(Exception e)
		{
			System.err.println("Exception in event parsing: Could not find node : " + name + " in the segment: " + nameOfParent + " for current xml fragment");

			return null;
		}
	}

	public int compare(Element left, Element right) 
	{
//		int sequenceLeft = getEventSequence(left);
//		int sequenceRight = getEventSequence(right);
//		
//		if(sequenceLeft != sequenceRight)
//		{
//			return new Integer(sequenceLeft).compareTo(sequenceRight);
//		}
		
		Calendar calLeft = getEventDate(left);
		Calendar calRight = getEventDate(right);
		return calLeft.compareTo(calRight);
	}
	
	public int getEventSequence(Element currentEvent)
	{
		Element eventHeader = getFirstChildByNameRegardlessOfNameSpace("gxsevent", currentEvent, "header");
		Element identifiers = getFirstChildByNameRegardlessOfNameSpace("header", eventHeader,"processidentifiers");
		Element sequenceId = getFirstChildByNameRegardlessOfNameSpace("identifiers", identifiers, "sequenceid");
		return Integer.parseInt(sequenceId.getText() );
	}
	
	public Calendar getEventDate(Element currentEvent)
	{
		Element eventHeader = getFirstChildByNameRegardlessOfNameSpace("gxsevent", currentEvent, "header");
		Element timestamp = getFirstChildByNameRegardlessOfNameSpace("header", eventHeader,"eventcreatetimestamp");
		String isoDateText = timestamp.getText();
		Calendar cal = javax.xml.bind.DatatypeConverter.parseDateTime(isoDateText);
		return cal;
	}
	
}
